use std::num::Wrapping;
use std::ops::{Add, Div, Mul, Rem, Sub};

pub trait Integer: Sized + PartialOrd + Ord + PartialEq + Eq + NumOps {}

macro_rules! empty_trait_impl {
  ($name:ident for $($t:ty)*) => ($(
    impl $name for $t {}
  )*)
}

empty_trait_impl!(Integer for usize u8 u16 u32 u64);
empty_trait_impl!(Integer for u128);

empty_trait_impl!(Integer for isize i8 i16 i32 i64);
empty_trait_impl!(Integer for i128);

pub trait NumOps<Rhs = Self, Output = Self>:
  Add<Rhs, Output = Output>
  + Sub<Rhs, Output = Output>
  + Mul<Rhs, Output = Output>
  + Div<Rhs, Output = Output>
  + Rem<Rhs, Output = Output>
{
}

impl<T, Rhs, Output> NumOps<Rhs, Output> for T where
  T: Add<Rhs, Output = Output>
    + Sub<Rhs, Output = Output>
    + Mul<Rhs, Output = Output>
    + Div<Rhs, Output = Output>
    + Rem<Rhs, Output = Output>
{
}

impl<T: Integer> Integer for Wrapping<T> where
  Wrapping<T>: Add<Output = Wrapping<T>>
    + Sub<Output = Wrapping<T>>
    + Mul<Output = Wrapping<T>>
    + Div<Output = Wrapping<T>>
    + Rem<Output = Wrapping<T>>
{
}
